package org.example.gateway.sync;

import com.alibaba.nacos.api.annotation.NacosInjected;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.ListView;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.example.gateway.contants.NacosConstants;
import org.example.gateway.enums.EnableEnum;
import org.example.gateway.pojo.NacosMetadata;
import org.example.gateway.pojo.dto.AppInfoDto;
import org.example.gateway.pojo.dto.ServiceInstance;
import org.example.gateway.service.AppService;
import org.example.gateway.utils.GatewayThreadFactory;
import org.example.gateway.utils.HttpUtil;
import org.example.gateway.utils.StrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 实时同步Nacos上注册的app信息
 */
@Configuration
public class NacosSyncListener implements ApplicationListener<ContextRefreshedEvent> {
    private static final Logger log = LoggerFactory.getLogger(NacosSyncListener.class);

    private static ScheduledThreadPoolExecutor scheduledPool = new ScheduledThreadPoolExecutor(1,
            new GatewayThreadFactory("nacos-sync", true).create());

    @NacosInjected
    private NamingService namingService;

    @Value("${nacos.discovery.server-addr}")
    private String baseUrl;

    @Autowired
    AppService appService;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if (event.getApplicationContext().getParent() != null) {
            return;
        }
        String url = "http://" + baseUrl + NacosConstants.INSTANCE_UPDATE_PATH;
        scheduledPool.scheduleWithFixedDelay(new NacosSyncTask(namingService, url, appService), 0, 30L, TimeUnit.SECONDS);
    }


    class NacosSyncTask implements Runnable {
        private NamingService namingService;

        private String url;

        private AppService appService;

        private Gson gson = new GsonBuilder().create();

        public NacosSyncTask(NamingService namingService, String url, AppService appService) {
            this.namingService = namingService;
            this.url = url;
            this.appService = appService;
        }

        /**
         * 定期更新权重，启用的插件，nacos实例的启用状态
         */
        @Override
        public void run() {
            try {
                ListView<String> services = namingService.getServicesOfServer(1, Integer.MAX_VALUE, NacosConstants.APP_GROUP_NAME);
                if (CollectionUtils.isEmpty(services.getData())) {
                    return;
                }
                List<String> appNames = services.getData();
                List<AppInfoDto> appInfos = appService.getAppInfos(appNames);
                for (AppInfoDto info : appInfos) {
                    if (CollectionUtils.isEmpty(info.getInstances())){
                        continue;
                    }
                    for (ServiceInstance instance : info.getInstances()) {
                        Map<String, Object> queryMap = buildQueryMap(info, instance);
                        String resp = HttpUtil.doPut(url, queryMap, "");
                        log.debug("resp:{}", resp);
                    }
                }
            } catch (Exception e) {
                log.error("nacos sync task error", e);
            }
        }

        private Map<String, Object> buildQueryMap(AppInfoDto appInfo, ServiceInstance instance) {
            Map<String, Object> map = new HashMap<>();
            map.put("serviceName", appInfo.getAppName());
            map.put("groupName", NacosConstants.APP_GROUP_NAME);
            map.put("ip", instance.getIp());
            map.put("port", instance.getPort());
            map.put("weight", instance.getWeight().doubleValue());
            NacosMetadata metadata = new NacosMetadata();
            metadata.setAppName(appInfo.getAppName());
            metadata.setVersion(instance.getVersion());
            metadata.setPlugins(String.join(",", appInfo.getEnabledPlugins()));
            map.put("metadata", StrUtil.urlEncoder(gson.toJson(metadata)));
            map.put("ephemeral", true);
            map.put("enabled", EnableEnum.ENABLE.getCode().equals(appInfo.getEnabled()));
            return map;
        }
    }
}
